package com.qcit.pach.plugin

import org.objectweb.asm.*
import org.objectweb.asm.commons.AdviceAdapter

class AParameterBindAdviceAdapter extends AdviceAdapter {

    String methodOwner
    String methodName
    int slotIndex
    boolean isBind //是否绑定
    boolean isLogin //是否登录
    String loginPath //登录路径
    String loginClass //登录路径
    MethodVisitor mv
    boolean isAbstractMethod //是否为抽象方法
    boolean isNativeMethod //是否为Native方法

    AParameterBindAdviceAdapter(int api, MethodVisitor mv, String owner, int access, String name, String desc) {
        super(api, mv, access, name, desc)
        this.methodOwner = owner
        this.methodName = name
        this.mv = mv
        this.isAbstractMethod = (access & Opcodes.ACC_ABSTRACT) == Opcodes.ACC_ABSTRACT
        this.isNativeMethod = (access & Opcodes.ACC_NATIVE) == Opcodes.ACC_NATIVE
    }

    @Override
    protected void onMethodEnter() {
        super.onMethodEnter()
        if (!isAbstractMethod && !isNativeMethod) {
            if (isBind) {
                println("onMethodEnter 这里可以改代码了")
                visitFieldInsn(GETSTATIC, "com/qcit/api/AParameterManager", "Companion", "Lcom/qcit/api/AParameterManager\$Companion;");
                visitMethodInsn(INVOKEVIRTUAL, "com/qcit/api/AParameterManager\$Companion", "getInstance", "()Lcom/qcit/api/AParameterManager;", false);
                visitVarInsn(ALOAD, 0);
                visitTypeInsn(CHECKCAST, "android/app/Activity");
                visitMethodInsn(INVOKEVIRTUAL, "com/qcit/api/AParameterManager", "load", "(Landroid/app/Activity;)V", false);

            }
            if (loginPath) {
                //插入登录检测
//            visitLdcInsn(loginClass);
//            visitMethodInsn(INVOKESTATIC, "java/lang/Class", "forName", "("+loginClass+";)Ljava/lang/Class;", false);
//            visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "newInstance", "()Ljava/lang/Object;", false);
//            visitVarInsn(ASTORE, 3);
//            visitVarInsn(ALOAD, 3);
//
//            Label label2 = new Label();
//            visitJumpInsn(IFNONNULL, label2);
//            visitTypeInsn(NEW, "java/lang/NullPointerException");
//            visitInsn(DUP);
//            visitLdcInsn("null cannot be cast to non-null type com.qcit.api.LoginIntercept");
//            visitMethodInsn(INVOKESPECIAL, "java/lang/NullPointerException", "<init>", "(Ljava/lang/String;)V", false);
//            visitInsn(ATHROW);
//            visitLabel(label2);
//           // visitFrame(Opcodes.F_APPEND, 2, new Object[]{Opcodes.TOP, "java/lang/Object"}, 0, null);
//            visitVarInsn(ALOAD, 3);
//            visitTypeInsn(CHECKCAST, "com/qcit/api/LoginIntercept");
//            visitVarInsn(ASTORE, 2);
//            Label label3 = new Label();
//            visitLabel(label3);
//            visitVarInsn(ALOAD, 2);
//            visitMethodInsn(INVOKEINTERFACE, "com/qcit/api/LoginIntercept", "isLogin", "()Z", true);
//            Label label4 = new Label();
//            visitJumpInsn(IFEQ, label4);
//            Label label5 = new Label();
//            visitLabel(label5);
//            visitFieldInsn(GETSTATIC, "com/qcit/api/ARouterManager", "Companion", "Lcom/qcit/api/ARouterManager\$Companion;");
//            visitMethodInsn(INVOKEVIRTUAL, "com/qcit/api/ARouterManager\$Companion", "getInstance", "()Lcom/qcit/api/ARouterManager;", false);
//            visitLdcInsn(loginPath);
//            visitMethodInsn(INVOKEVIRTUAL, "com/qcit/api/ARouterManager", "build", "(Ljava/lang/String;)Lcom/qcit/api/BundleManager;", false);
//            visitVarInsn(ALOAD, 0);
//            visitTypeInsn(CHECKCAST, "android/app/Activity");
//            visitMethodInsn(INVOKEVIRTUAL, "com/qcit/api/BundleManager", "navigation", "(Landroid/app/Activity;)Ljava/lang/Object;", false);
//            visitInsn(POP);
//            Label label6 = new Label();
//            visitLabel(label6);
//            visitMaxs(0,0)
//            visitInsn(RETURN);

                Label label0 = new Label();
                visitLabel(label0);
                //visitLineNumber(45, label0);
                visitLdcInsn(loginClass);
                visitMethodInsn(INVOKESTATIC, "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;", false);
                visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "newInstance", "()Ljava/lang/Object;", false);
                visitVarInsn(ASTORE, 2);
                visitVarInsn(ALOAD, 2);
                Label label1 = new Label();
                visitJumpInsn(IFNONNULL, label1);
                visitTypeInsn(NEW, "java/lang/NullPointerException");
                visitInsn(DUP);
                visitLdcInsn("null cannot be cast to non-null type com.qcit.api.LoginIntercept");
                visitMethodInsn(INVOKESPECIAL, "java/lang/NullPointerException", "<init>", "(Ljava/lang/String;)V", false);
                visitInsn(ATHROW);
                visitLabel(label1);
                // visitFrame(Opcodes.F_APPEND, 2, new Object[]{Opcodes.TOP, "java/lang/Object"}, 0, null);
                visitVarInsn(ALOAD, 2);
                visitTypeInsn(CHECKCAST, "com/qcit/api/LoginIntercept");
                visitVarInsn(ASTORE, 1);
                Label label2 = new Label();
                visitLabel(label2);
                visitVarInsn(ALOAD, 1);
                visitMethodInsn(INVOKEINTERFACE, "com/qcit/api/LoginIntercept", "isLogin", "()Z", true);
                Label label3 = new Label();
                visitJumpInsn(IFEQ, label3);
                Label label4 = new Label();
                visitLabel(label4);
                visitFieldInsn(GETSTATIC, "com/qcit/api/ARouterManager", "Companion", "Lcom/qcit/api/ARouterManager\$Companion;");
                visitMethodInsn(INVOKEVIRTUAL, "com/qcit/api/ARouterManager\$Companion", "getInstance", "()Lcom/qcit/api/ARouterManager;", false);
                visitLdcInsn(loginPath);
                visitMethodInsn(INVOKEVIRTUAL, "com/qcit/api/ARouterManager", "build", "(Ljava/lang/String;)Lcom/qcit/api/BundleManager;", false);
                visitVarInsn(ALOAD, 0);
                visitTypeInsn(CHECKCAST, "android/app/Activity");
                visitMethodInsn(INVOKEVIRTUAL, "com/qcit/api/BundleManager", "navigation", "(Landroid/app/Activity;)Ljava/lang/Object;", false);
                visitInsn(POP);
                Label label5 = new Label();
                visitLabel(label5);
                visitInsn(RETURN);
                visitLabel(label3);
                //visitFrame(Opcodes.F_FULL, 3, new Object[]{"com/qcit/work/TestActivity", "com/qcit/api/LoginIntercept", "java/lang/Object"}, 0, new Object[]{});
                visitInsn(RETURN);
                Label label6 = new Label();
                visitLabel(label6);
                visitLocalVariable("__utils", "Lcom/qcit/api/LoginIntercept;", null, label2, label6, 1);
                //  visitLocalVariable("this", methodOwner, null, label0, label6, 0);
                visitMaxs(3, 3);
                visitEnd();
            }
            //visitEnd()
//        for (MethodTimerEntity entity : StatisticPlugin.METHOD_TIMER_LIST) {
//            if (methodOwner.contains(entity.getOwner())) {
//                slotIndex = newLocal(Type.LONG_TYPE)
//                mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false)
//                mv.visitVarInsn(LSTORE, slotIndex)
//            }
//        }
        }
    }

    @Override
    void onMethodExit(int opcode) {
      //  if (!isAbstractMethod && !isNativeMethod) {
            if (opcode == Opcodes.ATHROW || (opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {
                if (isBind) {
                    println("onMethodExit 这里改完代码了")
//            Label label3 = new Label();
//            visitCode()
//            visitLabel(label3);
//            visitLineNumber(51, label3);

                }

                if (isBind)
                    isBind = false
                if (isLogin)
                    isLogin = false
//        for (MethodTimerEntity entity : StatisticPlugin.METHOD_TIMER_LIST) {
//            if (methodOwner.contains(entity.getOwner())) {
//                mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false)
//                mv.visitVarInsn(LLOAD, slotIndex)
//                mv.visitInsn(LSUB)
//                mv.visitVarInsn(LSTORE, slotIndex)
//                mv.visitVarInsn(LLOAD, slotIndex)
//                mv.visitLdcInsn(new Long(entity.getTime()))
//                mv.visitInsn(LCMP)
//                Label label0 = new Label()
//                mv.visitJumpInsn(IFLE, label0)
//                mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;")
//                mv.visitTypeInsn(NEW, "java/lang/StringBuilder")
//                mv.visitInsn(DUP)
//                mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false)
//                mv.visitLdcInsn(methodOwner + "/" + methodName + " --> execution time : (")
//                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false)
//                mv.visitVarInsn(LLOAD, slotIndex)
//                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;", false)
//                mv.visitLdcInsn("ms)")
//                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false)
//                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false)
//                mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false)
//                mv.visitLabel(label0)
//            }
//        }
            }
     //   }

        super.onMethodExit(opcode)
    }

    @Override
    AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
        if ("Lcom/qcit/annotation/AParameterBind;".equals(descriptor)){
            //这个类可以插入

            isBind = true
        }else if ("Lcom/qcit/annotation/ALogin;".equals(descriptor)){
            isLogin = true
            println("visitAnnotation ==== Lcom/qcit/annotation/ALogin")
            return new AnnotationValueVisitor(super.visitAnnotation(descriptor, visible))
        }

        return super.visitAnnotation(descriptor, visible)
    }
    class AnnotationValueVisitor extends AnnotationVisitor{

        public AnnotationValueVisitor( AnnotationVisitor annotationVisitor) {
            super(ASM7, annotationVisitor);
        }

        @Override
        public void visit(String name, Object value) {
            //System.out.println("注解"+name+value);
            super.visit(name, value);
            println("visit注解 ==== "+name+" : "+value)
            if ("path".equals(name))
                loginPath = value
            if ("clz".equals(name)){
//                String cls = value.toString().substring(1,value.toString().length() - 1)
//                cls = cls.replaceAll("/",".")
//                println("loginClass注解 ==== "+name+" : "+cls)
//                loginClass = cls
                Type type = Type.getType(value.toString())
                loginClass = type.getClassName()
            }
        }
    }

}
